import Carbon
import Cocoa
import SwiftUI

//
//  AppDelegate.swift
//  auto-switch-keyboard
//
//  Created by 月会 on 2024/9/26.
//

class AppDelegate: NSObject, NSApplicationDelegate {

    // 用来保存上一个获取焦点的应用
    var lastActiveApp: NSRunningApplication?
    // 保存不同app最后一次失去焦点时的输入法
    var appInputSources: [String: String] = [:]
    // 缓存输入法的id与对应关系
    var cacheInput: [String: TISInputSource] = [:]

    var statusBarItem: NSStatusItem?
    var statusBarMenu: NSMenu?  // 改为可选类型

    func applicationDidFinishLaunching(_ notification: Notification) {
        // 隐藏 dock 图标
        NSApp.setActivationPolicy(.accessory)

        // 创建状态栏图标
        statusBarItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
        if let button = statusBarItem?.button {
            button.image = NSImage(systemSymbolName: "keyboard", accessibilityDescription: "Auto Switch Keyboard")
        }

        // 创建状态栏菜单
        setupStatusBarMenu()

        // 缓存输入法的id与对应关系
        let inputSourceList =
            TISCreateInputSourceList(nil, false).takeUnretainedValue()
            as NSArray
        for inputSource in inputSourceList {
            let tisInputSource = inputSource as! TISInputSource
            let sourceID =
                Unmanaged<CFString>.fromOpaque(
                    TISGetInputSourceProperty(
                        tisInputSource, kTISPropertyInputSourceID)
                ).takeUnretainedValue() as String
            cacheInput[sourceID] = tisInputSource
        }

        // 监听应用程序切换
        NSWorkspace.shared.notificationCenter.addObserver(
            self,
            selector: #selector(activeApplicationDidChange),
            name: NSWorkspace.didActivateApplicationNotification,
            object: nil
        )

        // 保存当前活跃的应用
        lastActiveApp = NSWorkspace.shared.frontmostApplication
    }

    // 设置状态栏菜单
    func setupStatusBarMenu() {
        statusBarMenu = NSMenu()

        // 添加退出选项
        statusBarMenu?.addItem(
            NSMenuItem(
                title: "退出", action: #selector(quitApp), keyEquivalent: "q"))

        // 将菜单赋给状态栏项
        statusBarItem?.menu = statusBarMenu
    }

    // 退出应用程序
    @objc func quitApp() {
        NSApplication.shared.terminate(self)
    }

    @objc func activeApplicationDidChange(_ notification: Notification) {
        if let newApp = notification.userInfo?[
            NSWorkspace.applicationUserInfoKey] as? NSRunningApplication
        {
            // 如果前后应用相同，直接返回
            guard lastActiveApp != newApp else {
                return
            }

            // 处理旧应用失去焦点
            if let oldApp = lastActiveApp, oldApp != newApp {
                handleApplicationLostFocus(oldApp)
            }

            // 处理新应用获得焦点
            if let newAppName = newApp.localizedName,
                let savedInputSource = appInputSources[newAppName]
            {
                if getCurrentInputSourceName() != savedInputSource {
                    //                    print("获得焦点: \(newAppName)，切换到 \(savedInputSource)")
                    switchToInputSource(inputSourceID: savedInputSource)
                }
            }
            lastActiveApp = newApp
        }
    }

    func handleApplicationLostFocus(_ app: NSRunningApplication) {
        // 这里可以添加任何你希望在应用失去焦点时执行的逻辑
        if let appName = app.localizedName {
            if let currentInputSource = getCurrentInputSourceName() {
                appInputSources[appName] = currentInputSource
            }
        }

    }

    func getCurrentInputSourceName() -> String? {
        let inputSource = TISCopyCurrentKeyboardInputSource()
            .takeUnretainedValue()

        if let inputSourceNameCF = TISGetInputSourceProperty(
            inputSource, kTISPropertyInputSourceID)
        {
            let inputSourceName =
                Unmanaged<CFString>.fromOpaque(inputSourceNameCF)
                .takeUnretainedValue() as String
            return inputSourceName
        }
        return nil
    }

    //
    func switchToInputSource(inputSourceID: String) {
        guard let currentInputSourceID = getCurrentInputSourceName(),
            currentInputSourceID != inputSourceID
        else {
            return
        }

        if let inputRef = cacheInput[inputSourceID] {
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                TISSelectInputSource(inputRef)
            }
            return
        }
        print("Input source not found: \(inputSourceID)")
    }

}
